// 包声明，定义当前包名为server，属于service层
package server

// 导入依赖包
import (
	"encoding/json"
	"fmt"
	"github.com/go-gota/gota/dataframe"
	"github.com/go-gota/gota/series"
	"github.com/jinzhu/copier"
	"github.com/mitchellh/mapstructure"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"novel-go/global"
	"novel-go/middleware"
	"novel-go/model/request"
	"novel-go/model/response"
	"novel-go/model/server"
	"novel-go/utils"
	"strconv"
	"strings"
	"time"
)

// ApiService结构体定义，所有API服务方法都作为该结构体的方法实现
type ApiService struct {
}

// 账号密码登陆
// 账号密码登录
// 参数：req *request.Request 包含请求数据
// 返回值：(code int, data interface{}) 状态码和响应数据
func (apiService *ApiService) Login(req *request.Request) (code int, data interface{}) {
	// 将请求数据转为JSON
	jsonData, _ := json.Marshal(req.Data)

	// 反序列化为Login结构体
	var loginReq request.Login
	err := json.Unmarshal(jsonData, &loginReq)

	// 参数校验
	if nil != err {
		return response.InvalidParameter, nil
	}
	if len(loginReq.Username) == 0 || len(loginReq.Password) == 0 {
		return response.InvalidParameter, nil
	}

	// 检查数据库是否初始化
	if nil == global.FPG_DB {
		global.FPG_LOG.Error("db not init")
		return response.InternalServerError, nil
	}

	register := 0
	var user server.UsrAccount

	// 查询用户是否存在
	err = global.FPG_DB.Where("account = ?", loginReq.Username).First(&user).Error
	if nil != err {
		if err == gorm.ErrRecordNotFound {
			// 用户不存在，创建新用户
			register = 1
			user.Account = loginReq.Username
			user.Password = utils.EncodePassword(loginReq.Password, utils.GenSalt(8), 150000)
			user.Deviceid = loginReq.DeviceID
			err = global.FPG_DB.Create(&user).Error
			if nil != err {
				global.FPG_LOG.Error("create user", zap.Error(err))
				return response.InternalServerError, nil
			}
		} else {
			return response.UserNotFound, nil
		}
	} else {
		// 验证密码
		ok, err := utils.VerifyPassword(loginReq.Password, user.Password)
		if nil != err {
			global.FPG_LOG.Error("verify password error", zap.Error(err))
			return response.InternalServerError, nil
		}
		if !ok {
			return response.PasswordError, nil
		}
	}

	// 更新用户设备信息
	user.OS = loginReq.OS
	user.OSVersion = loginReq.OSVersion
	user.DeviceMode = loginReq.DeviceMode
	user.AppVersion = loginReq.AppVersion
	user.Country = loginReq.Country

	// 调用登录成功处理
	return apiService.LoginSuccess(&user, register, req.Context)
}

// 第三方平台授权登陆
// 第三方平台授权登录
func (apiService *ApiService) Auth(req *request.Request) (code int, data interface{}) {
	// 暂未实现
	return response.Success, nil
}

// 修改用户信息
func (apiService *ApiService) ModInfo(req *request.Request) (code int, data interface{}) {
	// 查询用户
	var user server.UsrAccount
	// 使用gorm查询，查询条件是id等于req.UserID
	err := global.FPG_DB.Where("id = ?", req.UserID).First(&user).Error
	if nil != err {
		return response.UserNotFound, nil
	}

	// 不允许更新的用户字段
	filterMap := map[string]bool{
		"id":       true,
		"account":  true,
		"password": true,
		"vip":      true,
		"viptime":  true,
		"score":    true,
		"logtime":  true,
		"status":   true,
	}

	// 解析修改请求
	// 类型断言 将req.Data转换为map[string]interface{}
	// 语法：value, ok := x.(T)
	// 检查接口值x是否是类型T
	// 如果是，返回T类型的值；否则返回零值
	modReq, ok := req.Data.(map[string]interface{})
	if !ok {
		return response.InvalidParameter, nil
	}

	// 检查是否尝试修改不允许的字段
	for key := range modReq {
		if _, ok := filterMap[key]; ok {
			return response.InvalidParameter, nil
		}
	}

	// 将map转换为结构体
	var updUser server.UsrAccount
	if err := mapstructure.Decode(modReq, &updUser); err != nil {
		return response.InvalidParameter, nil
	}

	// 更新用户信息
	// Model(&user)：指定要更新的模型和条件（基于user.ID）
	// 更新user结构体中的所有字段（非零字段）
	// 通过前面过滤来决定是否更新字段
	global.FPG_DB.Model(&user).Updates(updUser)
	// 更新user结构体中的指定字段
	//global.FPG_DB.Model(&user).Select([]string{"id", "nickname", "gender", "photourl", "provider", "pid"}).Updates(updUser)

	return response.Success, nil
}

// 获取子账号列表
func (apiService *ApiService) BindList(req *request.Request) (code int, data interface{}) {
	// 暂未实现
	return response.Success, nil
}

// 获取用户信息
func (apiService *ApiService) GetInfo(req *request.Request) (code int, data interface{}) {
	var user server.UsrAccount
	err := global.FPG_DB.Where("id = ?", req.UserID).First(&user).Error
	if nil != err {
		return response.UserNotFound, nil
	}

	return response.Success, user
}

// 获取书籍列表
func (apiService *ApiService) BookList(req *request.Request) (code int, data interface{}) {
	// 调用 缓存 中间件 传入 业务函数 和 缓存超时时间	业务函数在缓存不存在的时候生效
	// 失效时间 3600 秒
	return middleware.CacheMemoize(SearchBook, 3600, req)
}

// 获取获取书架
//func (apiService *ApiService) GetFavorites(req *request.Request) (code int, data interface{}) {
//	books := make([]server.ResBook, 0)
//	sql := fmt.Sprintf("SELECT b.* FROM usr_favorites a JOIN res_book b ON a.bid = b.id WHERE a.uid = %d AND a.`status` = 1", req.UserID)
//	err := global.FPG_DB.Raw(sql).Scan(&books).Error
//	if nil != err {
//		global.FPG_LOG.Error("execute sql error", zap.Error(err))
//		return response.InternalServerError, nil
//	}
//
//	return response.Success, books
//}

// 定义获取收藏书籍列表的API方法
// 参数：req - 包含用户ID等请求信息
// 返回值：状态码和书籍列表数据
func (apiService *ApiService) GetFavorites(req *request.Request) (code int, data interface{}) {
	// 初始化一个ResBook类型的空切片，用于存储查询结果
	books := make([]server.ResBook, 0)

	// 构建SQL查询语句：
	// 1. 从usr_favorites表(别名a)和res_book表(别名b)联查
	// 2. 关联条件：a.bid = b.id
	// 3. 查询条件：用户ID匹配且收藏状态为1(表示有效收藏)
	sql := fmt.Sprintf("SELECT b.* FROM usr_favorites a JOIN res_book b ON a.bid = b.id WHERE a.uid = %d AND a.`status` = 1", req.UserID)

	// 执行原生SQL查询：
	// 1. 使用Raw()执行原生SQL
	// 2. 使用Scan()将结果扫描到books切片
	// 3. 获取可能的错误
	err := global.FPG_DB.Raw(sql).Scan(&books).Error

	// 错误处理：如果查询出错
	if nil != err {
		// 记录错误日志，包含具体错误信息
		global.FPG_LOG.Error("execute sql error", zap.Error(err))
		// 返回500服务器错误状态码
		return response.InternalServerError, nil
	}

	// 成功返回：
	// 1. 返回200成功状态码
	// 2. 返回查询到的书籍列表
	return response.Success, books
}

// 收藏书籍
func (apiService *ApiService) SetFavorites(req *request.Request) (code int, data interface{}) {
	jsonData, _ := json.Marshal(req.Data)
	var setFavoritesReq request.SetFavorites
	err := json.Unmarshal(jsonData, &setFavoritesReq)
	if nil != err {
		return response.InvalidParameter, nil
	}
	tx := global.FPG_DB.Begin()
	for _, v := range setFavoritesReq.Bids {
		sql := fmt.Sprintf("INSERT INTO usr_favorites(uid, bid, `status`) VALUES (%d, %d, %d) ON DUPLICATE KEY UPDATE `status` = %d, UpdateTime = CURRENT_TIMESTAMP", req.UserID, v, setFavoritesReq.Status, setFavoritesReq.Status)
		err := tx.Exec(sql).Error
		if nil != err {
			global.FPG_LOG.Error("execute sql error", zap.Error(err))
			tx.Rollback()
			return response.InternalServerError, nil
		}
	}
	tx.Commit()

	return response.Success, nil
}

// 获取热搜关键词
func (apiService *ApiService) HotKeywords(req *request.Request) (code int, data interface{}) {
	return middleware.CacheMemoize(GetHotKeywords, 3600, req)
}

// 获取书籍类目
func (apiService *ApiService) GetCategories(req *request.Request) (code int, data interface{}) {
	return middleware.CacheMemoize(GetBookCategories, 3600, req)
}

// 获取任务列表
func (apiService *ApiService) QuestList(req *request.Request) (code int, data interface{}) {
	// 初始化任务切片
	quests := make([]server.UsrQuest, 0)

	// 查询用户当前所有任务记录
	err := global.FPG_DB.Where("uid = ?", req.UserID).Find(&quests).Error
	if nil != err {
		return response.InternalServerError, nil
	}

	// 获取当天日期字符串（格式：YYYY-MM-DD）
	today := time.Now().Format("%Y-%m-%d")

	// 将用户任务转为map格式，key为任务ID
	questMap := make(map[string]server.UsrQuest, 0)
	for _, v := range quests {
		questMap[v.Tid] = v
	}

	// 准备最终返回结果
	results := make([]map[string]interface{}, 0)
	// 利用数据库的事务特性，批量更新任务状态
	tx := global.FPG_DB.Begin()

	// 遍历全局任务模板
	for _, v := range global.FPG_QUEST.Maps() {
		// 复制任务模板数据
		questDoing := make(map[string]interface{}, 0)
		copier.Copy(&questDoing, &v)

		// 解析任务参数
		paramsArr := strings.Split(v["Param"].(string), "|")

		// 检查用户是否有该任务记录 从任务模板中取出 任务ID转化为string
		questObj, ok := questMap[v["Task ID"].(string)]
		if ok {
			// 处理不同类型的任务状态
			switch questDoing["Task Type"] {
			case "daily": // 每日任务
				if questObj.UpdateTime.Format("%Y-%m-%d") != today {
					questObj.Status = 0
					questObj.UpdateTime = time.Now()
				}
			case "loop": // 日环任务 支持多日连续进度累积
				if questObj.UpdateTime.Format("%Y-%m-%d") != today {
					// 如果任务进度已经达到最大值，则重置任务进度为0，否则递增任务进度
					if questObj.Progress+1 >= len(paramsArr) ||
						(time.Since(questObj.UpdateTime).Hours()/24) > 1 {
						questObj.Progress = 0
					} else {
						questObj.Progress += 1
					}
					questObj.Status = 0
					questObj.UpdateTime = time.Now()
				}
			case "weekly": // 每周任务
				// 比较当前周数和最后更新周数，如果不相等，则重置任务进度为0，否则递增任务进度
				if utils.WeekByDate(time.Now()) > utils.WeekByDate(questObj.UpdateTime) {
					questObj.Status = 0
					questObj.Progress = 0
					questObj.UpdateTime = time.Now()
				}
			}

			// 更新任务状态信息
			questDoing["status"] = questObj.Status
			questDoing["progress"] = questObj.Progress

			// 更新数据库记录
			tx.Model(&questObj).Updates(server.UsrQuest{
				Status:     questObj.Status,
				Progress:   questObj.Progress,
				UpdateTime: questObj.UpdateTime,
			})
		} else {
			// 新任务默认状态
			questDoing["status"] = 0
			questDoing["progress"] = 0
		}

		// 提交数据库事务
		tx.Commit()
		// 将任务数据添加到最终返回结果中
		results = append(results, questDoing)
	}

	return response.Success, results
}

// 领取任务奖励
func (apiService *ApiService) QuestReward(req *request.Request) (code int, data interface{}) {
	tid, ok := req.Data.(map[string]interface{})["tid"].(string)
	if !ok || len(tid) == 0 {
		return response.InvalidParameter, nil
	}

	var user server.UsrAccount
	err := global.FPG_DB.Where("id = ?", req.UserID).First(&user).Error
	if nil != err {
		return response.UserNotFound, nil
	}

	return apiService.ReceiveQuestReward(&user, tid)
}

// 上报阅读记录
func (apiService *ApiService) ReadLog(req *request.Request) (code int, data interface{}) {
	jsonData, _ := json.Marshal(req.Data)
	var usrReadLogReq server.UsrReadLog
	err := json.Unmarshal(jsonData, &usrReadLogReq)
	if nil != err {
		return response.InvalidParameter, nil
	}
	// 如果阅读时间 小于等于 0 则返回错误
	if usrReadLogReq.Duration <= 0 {
		return response.InvalidParameter, nil
	}
	var user server.UsrAccount
	err = global.FPG_DB.Where("id = ?", req.UserID).First(&user).Error
	if nil != err {
		return response.UserNotFound, nil
	}

	tid := "task.read.duration"

	// 从全局任务模板中筛选特定任务
	// 使用dataframe.Filter方法，筛选条件为：
	// - 列名："Task ID"
	// - 比较操作：等于(Eq)
	// - 比较值：任务ID(tid)
	questTpl := global.FPG_QUEST.Filter(
		dataframe.F{
			Colname:    "Task ID",
			Comparator: series.Eq,
			Comparando: tid,
		},
	)
	// 从任务模板中获取Param参数列的所有记录 13|30|90|180|360|600, 10|20|30|40|50|60
	params := questTpl.Select("Param").Records()
	// 将参数值按"|"分割成数组，用于后续处理任务参数
	paramsArr := strings.Split(params[1][0], "|")

	// 从任务模板中获取Virtual Currency(虚拟货币)列的所有记录
	score := questTpl.Select("Virtual Currency").Records()
	// 将虚拟货币值按"|"分割成数组，用于后续处理任务奖励  13|30|90|180|360|600, 10|20|30|40|50|60
	scoreArr := strings.Split(score[1][0], "|")

	// 增加用户阅读时长
	user.Duration += uint(usrReadLogReq.Duration)

	// 查询用户当前任务进度
	var quest server.UsrQuest
	questErr := global.FPG_DB.Where("uid = ? AND tid = ?", req.UserID, tid).First(&quest).Error
	progress := -1
	if nil != questErr {
		progress = quest.Progress
	}

	// 开始数据库事务
	tx := global.FPG_DB.Begin()

	// 遍历任务参数，检查是否达到奖励条件
	for i := progress + 1; i < len(paramsArr); i++ {
		// 将参数转换为整数
		v, _ := strconv.Atoi(paramsArr[i])

		// 检查用户阅读时长是否达到当前奖励条件
		if int(user.Duration) >= v {
			// 计算奖励积分
			score, _ := strconv.Atoi(scoreArr[i])
			score_before := user.Score
			user.Score = score_before + uint(score)

			// 创建积分记录
			usrScore := server.UsrScore{
				Uid:         req.UserID,
				Score:       score,
				ScoreBefore: score_before,
				ScoreAfter:  user.Score,
				SourceId:    tid,
				Remark:      "阅读奖励",
			}
			err = global.FPG_DB.Create(&usrScore).Error
			if nil != err {
				global.FPG_LOG.Error("create user score", zap.Error(err))
				tx.Callback()
				return response.InternalServerError, nil
			}

			// 更新或创建任务记录
			if nil != questErr {
				// 创建新任务记录
				quest = server.UsrQuest{
					Uid:      uint(req.UserID),
					Tid:      tid,
					Status:   1,
					Progress: i,
				}
				err = global.FPG_DB.Create(&quest).Error
				if nil != err {
					global.FPG_LOG.Error("create user quest", zap.Error(err))
					tx.Callback()
					return response.InternalServerError, nil
				}
			} else {
				// 更新现有任务记录
				tx.Model(&quest).Updates(server.UsrQuest{
					Status:     1,
					Progress:   i,
					UpdateTime: time.Now(),
				})
			}
		}
	}
	err = global.FPG_DB.Create(&usrReadLogReq).Error
	if nil != err {
		global.FPG_LOG.Error("create user read log", zap.Error(err))
		tx.Callback()
		return response.InternalServerError, nil
	}
	tx.Model(&user).Updates(server.UsrAccount{
		Score:    user.Score,
		Duration: user.Duration,
	})
	tx.Commit()

	results := make(map[string]uint)
	results["score"] = user.Score
	results["duration"] = user.Duration

	return response.Success, results
}

// 检查消息
func (apiService *ApiService) CheckMessage(req *request.Request) (code int, data interface{}) {
	// 暂未实现
	return response.Success, nil
}

// 获取书币流水
func (apiService *ApiService) ScoreFlow(req *request.Request) (code int, data interface{}) {
	page, _ := req.Data.(map[string]interface{})["num"].(float64)
	num, _ := req.Data.(map[string]interface{})["num"].(float64)
	if num == 0 {
		num = 30
	}
	usrScores := make([]server.UsrScore, 0)
	err := global.FPG_DB.Limit(int(num)).Offset(int(page*num)).Find(&usrScores).Where("uid = ?", req.UserID).Error
	if nil != err {
		return response.InternalServerError, nil
	}

	return response.Success, usrScores
}

// 购买章节
func (apiService *ApiService) BuyChapter(req *request.Request) (code int, data interface{}) {

	// 类型断言：尝试将请求数据转换为BuyChapter结构体 用点来表示
	// ok表示类型断言是否成功
	buyChapterReq, ok := req.Data.(request.BuyChapter)
	if !ok {
		return response.InvalidParameter, nil
	}
	var user server.UsrAccount
	err := global.FPG_DB.Where("id = ?", req.UserID).First(&user).Error
	if nil != err {
		return response.UserNotFound, nil
	}

	if buyChapterReq.Score > 0 {
		// 开始数据库事务
		tx := global.FPG_DB.Begin()

		// 计算扣除的积分值
		score := -buyChapterReq.Score
		// 记录扣除前的积分
		score_before := user.Score
		// 计算扣除后的积分
		user.Score = score_before + uint(score)
		// 更新用户积分
		tx.Model(&user).Update("score", user.Score)

		// 创建积分变动记录
		usrScore := server.UsrScore{
			Uid:         req.UserID,
			Score:       score,
			ScoreBefore: score_before,
			ScoreAfter:  user.Score,
			SourceId:    strconv.Itoa(buyChapterReq.Cid),
			Remark:      "购买章节",
		}
		err = global.FPG_DB.Create(&usrScore).Error
		if nil != err {
			global.FPG_LOG.Error("create user score", zap.Error(err))
			tx.Callback()
			return response.InternalServerError, nil
		}

		// 创建用户章节购买记录 绑定购买的章节
		usrChapter := server.UsrChapter{
			Uid: req.UserID,        // 用户	ID
			Bid: buyChapterReq.Bid, // 书籍ID
			Cid: buyChapterReq.Cid, // 章节ID
		}
		err = global.FPG_DB.Create(&usrChapter).Error
		if nil != err {
			global.FPG_LOG.Error("create user chapter", zap.Error(err))
			tx.Callback()
			return response.InternalServerError, nil
		}

		// 提交事务
		tx.Commit()
	}

	results := make(map[string]uint)
	results["score"] = user.Score

	return response.Success, results
}

// 获取用户已购买章节
// UserChapter 获取用户已购买章节
// 参数：
//
//	req *request.Request - 包含用户ID和书籍ID的请求对象
//
// 返回值：
//
//	code int - 响应状态码
//	data interface{} - 响应数据，成功时返回章节列表
func (apiService *ApiService) UserChapter(req *request.Request) (code int, data interface{}) {
	// 从请求数据中获取书籍ID(bid)并转换为float64类型
	bid, ok := req.Data.(map[string]interface{})["bid"].(float64)
	if !ok {
		// 如果获取书籍ID失败，返回参数错误
		return response.InvalidParameter, nil
	}

	// 初始化章节切片
	chapters := make([]server.UsrChapter, 0)

	// 查询数据库：根据用户ID和书籍ID查询已购买章节
	err := global.FPG_DB.Where("uid = ? AND bid = ?", req.UserID, int(bid)).Find(&chapters).Error
	if nil != err {
		// 如果查询出错，记录错误日志并返回服务器错误
		global.FPG_LOG.Error("execute sql error", zap.Error(err))
		return response.InternalServerError, nil
	}

	// 查询成功，返回章节列表
	return response.Success, chapters
}
